package soot.we.android.component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import soot.Body;
import soot.MethodOrMethodContext;
import soot.Scene;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.Unit;
import soot.jimple.toolkits.callgraph.CHATransformer;
import soot.jimple.toolkits.callgraph.CallGraph;
import soot.jimple.toolkits.callgraph.Targets;
import soot.tagkit.Tag;
import soot.toolkits.graph.ExceptionalUnitGraph;
import soot.toolkits.graph.UnitGraph;
import soot.util.Chain;
import soot.we.android.XML.EntityApplicationBase;
import soot.we.android.XML.EntityComponent;
import soot.we.android.log.AlarmLog;
import soot.we.android.test.Test;

public class MySceneTransformer extends soot.SceneTransformer {
	public static List<EntityMethod> eCfgList;

	@Override
	protected void internalTransform(String arg0, Map<String, String> arg1) {
		CHATransformer.v().transform();

		EntityApplicationBase application = Test.app;

		ProcessJimpleClass.findClassLayoutMappings();
		
		ArrayList<EntityComponent> aListComp = application.getComponents();
		for (EntityComponent ec : aListComp) {
			EntityClass eClass = new EntityClass();
			eCfgList = new ArrayList<EntityMethod>();
			String compName = ec.getComponnetName();
			eClass.setIsEntryPoint(ec.getExported());
			eClass.setCompName(compName);
			System.out.println("instrumenting Component: " + compName);
			//****start extracting CFG of Each method in certain Class*********
			ConstructClass(compName,eClass);
			//****end extracting CFG********
			eClass.setMethodList(eCfgList);
			ProcessJimpleClass.classCfgList.add(eClass);
			// ************* if it is outerClass **********
			List<String> innerClassList = ProcessJimpleClass
					.findAnonyInnerClass(compName);
			if (innerClassList.size() > 0) {
				for (String innerClass : innerClassList) {
					eClass = new EntityClass();
					eCfgList = new ArrayList<EntityMethod>();
					eClass.setIsEntryPoint(false);
					eClass.setCompName(innerClass);
					System.out.println("instrumenting InnerClass: " + compName);
					//****start extracting CFG of Each method in inner Class*********
					ConstructClass(innerClass,eClass);
					//****end extracting CFG***************
					eClass.setMethodList(eCfgList);
					ProcessJimpleClass.classCfgList.add(eClass);

				}
			}
			// *************outerClass end**************

		}
	
	}

	private void ConstructClass(String ClassName,EntityClass eClass) {

		SootClass sootClass = Scene.v().loadClassAndSupport(ClassName);
		sootClass.setApplicationClass();
		
		List<EntityVariable> tempclassVariables = new ArrayList<EntityVariable>();
		for (SootField sf : sootClass.getFields()) {
			EntityVariable tempVariable = new EntityVariable(sf.getType()
					.toString(), sf.getSubSignature());
			tempclassVariables.add(tempVariable);
		}
		eClass.setClassVariable(tempclassVariables);
		
		List<SootMethod> methodList = sootClass.getMethods();
		for (SootMethod sm : methodList) {
			Body body = sm.retrieveActiveBody();
			// Instruments bytecode
			new MyBodyTransformer().transform(body);
		}
	}
}
// **********************start*******
// SootClass sootClass =
// Scene.v().getSootClass("com.example.mytest.MainActivity");
// sootClass.setApplicationClass();
// SootMethod onCreate = sootClass.getMethodByName("onCreate");
//
// List<SootMethod> entryPoints = Arrays.asList(onCreate);
// Scene.v().setEntryPoints(entryPoints);
// CallGraph cg = Scene.v().getCallGraph();
//
// Iterator<MethodOrMethodContext> targets = new
// Targets(cg.edgesOutOf(onCreate));
// while (targets.hasNext()) {
// SootMethod tgt = (SootMethod)targets.next();
// System.out.println(onCreate + " may call " + tgt);
// }

// List<EntityVariable> tempclassVariables = new
// ArrayList<EntityVariable>();
// for(SootField sf :sootClass.getFields()){
// EntityVariable tempVariable = new
// EntityVariable(sf.getType().toString(),sf.getSubSignature());
// tempclassVariables.add(tempVariable);
// for(Tag tag:sf.getTags()){
// System.out.println("-----------------tags: "+tag.toString());
// }
// }
// if(tempclassVariables.size()>0)
// eClass.setClassVariable(tempclassVariables);

// ************************end************

// for(Tag tag:sf.getTags()){
// System.out.println("-----------------tags: "+tag.toString());
// }
// }
// if(tempclassVariables.size()>0)
// eClass.setClassVariable(tempclassVariables);

// SootMethod sootMethod = sootClass.getMethodByName("testmain");

// Body b = sootMethod.retrieveActiveBody();
// UnitGraph g = new ExceptionalUnitGraph(b);
// LiveVariablesAnalysis lv = new LiveVariablesAnalysis(g);
// // return HashSets
// // of live variables:
// for (Unit s : g) {
// lv.getFlowBefore(s);
// lv.getFlowAfter(s);
// }

// ***********************
// Body b = sootMethod.retrieveActiveBody();
// UnitGraph g = new ExceptionalUnitGraph(b);
// MyFwdAnalysis an = new MyFwdAnalysis(g);
// for (Unit unit : g) {
// FlowSet in = an.getFlowBefore(unit);
// FlowSet out = an.getFlowAfter(unit);
// }
// ***************************